const dgram = require("dgram");
const server = dgram.createSocket("udp4");
const fallbackServer = "100.125.1.250"; //上级DNS服务器
const dns_middle = require("../middle/dns_contract_middle");
const config = require("./config.json");
const vote_middle = require("../middle/first_vote_contract");
const connector = require("./vote_server");
const to = (promise) => {
  return promise.then((res) => [null, res]).catch((error) => [error]);
};
var Web3 = require("web3");
var web3 = new Web3(config.http_rpc_node);
const fs = require("fs");
const EventEmitter = require("events").EventEmitter;
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 注册事件，缓存未命中
myEmitter.on("missed", async (domain) => {
  [err, result] = await to(
    addToFile("missed.json", JSON.stringify({ domain: domain }))
  );
  if (err) {
    console.log(err);
  } else {
    //触发投票合约
    console.log("触发投票");
    var web3 = new Web3(config.ws_rpc_node);
    var abi = JSON.parse(config.vote_contract.abi);
    var bytecode = config.vote_contract.bytecode;
    var account = config.account;
    var password = config.password;
    var voter_list = config.vote_contract.voter_list;
    var duration = config.vote_contract.duration;
    var init_vote_contract = new web3.eth.Contract(abi, {
      from: account, // default from address
      gasPrice: "1",
    });
    [unlock_err, unlock_res] = await to(
      vote_middle.unlock(web3, account, password)
    );
    if (unlock_err) {
      console.log(unlock_err);
      console.log("发布投票---解锁账户---失败");
    } else {
      [deploy_err, deploy_res] = await to(
        vote_middle.deploy_first_voting(
          domain,
          voter_list,
          duration,
          init_vote_contract,
          bytecode,
          account
        )
      );
      if (deploy_err) {
        console.log(deploy_err);
        console.log("发布投票---部署合约---失败");
      } else {
        console.log("发布投票---部署合约---成功");
        var address_list = config.vote_sender.node_list;
        var port = config.vote_sender.vote_send_port;
        console.log("合约地址为 %s", deploy_res);
        // 向参与投票的账户所在地址传递合约信息
        connector.sendVoteInfo(address_list, domain, deploy_res, port);
        // 向监控节点传递合约地址
        connector.sendWitnessInfo(
          deploy_res,
          config.vote_contract.abi,
          config.witness_sender.witness_address,
          config.witness_sender.witness_port
        );
        setTimeout(finishedAlert, 70000, deploy_res, domain);
      }
    }
  }
});
myEmitter.on("finished", async (contract_address, domain) => {
  console.log("获取投票信息：");
  var abi = JSON.parse(config.vote_contract.abi);
  var vote_contract = new web3.eth.Contract(abi, contract_address);
  [err, res] = await to(vote_middle.getMostIp(vote_contract));
  if (err) {
    console.log("获取投票信息---失败");
    console.log(err);
  } else {
    switch (res) {
      case "error_1":
        console.log("当前投票还未结束");
        break;
      case "error_2":
        console.log("没有得票数较高的ip");
        break;
      default:
        console.log("获取投票信息 --- 成功，最高ip为%s", res);
        console.log("写入域名合约");
        var dns_abi = JSON.parse(config.dns_contract.abi);
        var dns_contract_address = config.dns_contract.contract_address;
        var dns_contract = new web3.eth.Contract(dns_abi, dns_contract_address);
        var account = config.account;
        var password = config.password;
        [unlock_err, unlock_res] = await to(
          vote_middle.unlock(web3, account, password)
        );
        if (unlock_err) {
          console.log("写入域名合约 --- 解锁失败");
        } else {
          [dns_err, dns_result] = await to(
            dns_middle.addRecordToChain(
              dns_contract,
              account,
              password,
              domain,
              res
            )
          );
          if (dns_err) {
            if (dns_err.toString().indexOf("unlock") != -1) {
              console.log("写入域名合约 --- 失败 ");
              console.log("尝试第二次写入");
              [sec_err, sec_result] = await to(
                dns_middle.addRecordToChain(
                  dns_contract,
                  account,
                  password,
                  domain,
                  res
                )
              );
              if(sec_err){
                console.log("写入域名合约 --- 第二次尝试 --- 失败 ");
                console.log(sec_err);
              }else{
                console.log("写入域名合约 --- 第二次尝试 --- 成功 ");
                console.log(sec_result);
              }
            }else{
              console.log("写入域名合约 --- 失败");
              console.log(dns_err);
            }
          } else {
            console.log("写入域名合约 --- 成功");
            console.log(dns_result);
          }
        }
    }
  }
});
function finishedAlert(contract_address, domain) {
  myEmitter.emit("finished", contract_address, domain);
}
function forward(msg, rinfo) {
  const client = dgram.createSocket("udp4");
  client.on("error", (err) => {
    console.log(`client error:` + err.stack);
    client.close();
  });
  client.on("message", (fMsg) => {
    console.log("接收到上级域名服务器反馈，转发到load_balancer");
    server.send(fMsg, rinfo.port, rinfo.address, (err) => {
      err && console.log(err);
    });
    client.close();
  });
  client.send(msg, 53, fallbackServer, (err) => {
    console.log("区块链缓存未命中，转发到上级域名服务器%s", fallbackServer);
    if (err) {
      console.log(err);
      client.close();
    }
  });
}

function parseHost(msg) {
  //转换域名
  let num = msg[0];
  let offset = 1;
  let host = "";
  while (num !== 0) {
    host += msg.slice(offset, offset + num).toString();
    offset += num;
    num = msg[offset];
    offset += 1;
    if (num !== 0) host += ".";
  }
  return host;
}

function resolve(ip, msg, rinfo) {
  //响应
  let len = msg.length;
  let templet = [192, 12, 0, 1, 0, 1, 0, 0, 0, 218, 0, 4].concat(
    ip.split(".").map((i) => Number(i))
  ); //<===可以自定义
  const response = new ArrayBuffer(len + 16);
  var bufView = new Uint8Array(response);
  for (let i = 0; i < msg.length; i++) bufView[i] = msg[i];
  for (let i = 0; i < templet.length; i++) bufView[msg.length + i] = templet[i];
  bufView[2] = 129;
  bufView[3] = 128;
  bufView[7] = 1;
  server.send(bufView, rinfo.port, rinfo.address, (err) => {
    if (err) {
      console.log(err);
      server.close();
    }
  });
}
function addToFile(file_name, data) {
  return new Promise((resolve, reject) => {
    fs.appendFile(file_name, data, (err) => {
      if (err) {
        reject(err);
      } else {
        resolve("Success");
      }
    });
  });
}

// 链上查询域名记录
server.on("message", async (msg, rinfo) => {
  // 从消息中解析待查询的域名
  let host = parseHost(msg.slice(12));
  console.log("收到来自load_balancer的转发请求，查询   %s   对应的域名", host);
  var abi = JSON.parse(config.dns_contract.abi);
  var contract_address = config.dns_contract.contract_address;
  var dnsContract = new web3.eth.Contract(abi, contract_address);
  // 在区块链中查询域名对应的IP地址
  let ip = await dns_middle.getIPFromChain(host, dnsContract);
  // 区块链是否命中
  if (ip.length !== 0) {
    console.log("区块链缓存命中", host, "==>", ip);
    resolve(ip, msg, rinfo); //解析与响应
  } else {
    myEmitter.emit("missed", host);
    forward(msg, rinfo); //转发
  }
});

server.on("error", (err) => {
  console.log("server error:" + err.stack);
  server.close();
});
server.on("listening", () => {
  const addr = server.address();
  console.log(`run ${addr.address}:${addr.port}`);
});
server.bind(53);
